﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml.Serialization;

namespace NetMessage
{
    /// <summary>
    /// 创建网络消息
    /// </summary>
    public class CreateMessage
    {
        #region 代码模板

        /// <summary>
        /// 代码模板
        /// </summary>
        private const string codeTemplte = @"
#pragma once
#include `Package.h`
#include `NetQueue.h`

//	#PackageInfo#
class #Direction#_#Name#: public Package
{
public:
	#Direction#_#Name#(int msgId, int playerId):Package(msgId, playerId)
	{

	}
public:
#paramList#

	//	读取数据
	bool Read(TMessage * data)
	{
		int len = sizeof(TMsgHead) #packageSize# + sizeof(MsgEndFlag);
		if (data->len != len)
			return false;

		TMsgHead* head = (TMsgHead*)data->p;

		//	获得实际数据的偏移量
#readParam#

		return true;
	}

	//	获得数据包的数据
	//	注意：这里返回的字符串需要人工释放
	TMessage GetBuf()
	{
		int len = sizeof(TMsgHead)#packageSize# + sizeof(MsgEndFlag);
		char* buf = new char[len];

		TMsgHead* head = (TMsgHead*)buf;
		head->len = len;
		head->userID = this->m_playerId;
		head->type = 10;
		head->ID = 0;

#setParamBuf#

		AddMsgEnd(buf + sizeof(TMsgHead)#packageSize#);
		TMessage msg;

		msg.p = buf;
		msg.len = len;
		return msg;
	}

	// 执行数据包的脚本功能
	bool Execute(CPlayerSession* player);

	//	发送数据包
	static bool SendPackage(int playerId#sendPackageParam#)
	{
		CG_Despoit * package = new CG_Despoit(1000, playerId);
#setSendPackage#

		//	将要发送的数据放入发送队列
		g_sendQueue.Put(package);
	}
};
";

        #endregion

        public string Create(Message msg)
        {
            var builder = new StringBuilder();

            builder.Append(codeTemplte);
            builder.Replace("#Name#", msg.Name);
            builder.Replace("#PackageInfo#", msg.Info);
            builder.Replace("#Direction#", GetDirection(msg));
            builder.Replace("#paramList#", GetParamLists(msg));
            builder.Replace("#packageSize#", GetPackageSize(msg));
            builder.Replace("#readParam#", GetReadCode(msg));
            builder.Replace("#setParamBuf#", SetSendBufCode(msg));
            builder.Replace("#sendPackageParam#", GetSendPackageParam(msg));
            builder.Replace("#setSendPackage#", GetPackageSet(msg));

            builder.Replace("`", "\"");

            return builder.ToString();
        }

        public void Test()
        {
            var ser = new XmlSerializer(typeof (List<Message>));
            const string fileName = @"D:\MyGame\贸易时代\tradeage\Tools\CreateNetMessage\CreateNetMessage\bin\Debug\Message.xml";
            const string clientFolder = @"D:\MyGame\贸易时代\tradeage\server\GameServer\Net";

            using (var stream = new FileStream(fileName, FileMode.Open))
            {
                var messages = ser.Deserialize(stream) as List<Message>;
                if (messages != null)
                    foreach (Message msg in messages)
                    {
                        if (msg.Direction == MessageDirection.Client2GameServer)
                        {
                            string ret = Create(msg);
                            //Console.WriteLine(ret);
                            if (File.Exists(Path.Combine(clientFolder, GetFileNameHead(msg) + ".cpp")))
                            {
                            }
                            string headFileName = GetFileNameHead(msg) + ".h";
                            if (!string.IsNullOrEmpty(msg.Gruop))
                                headFileName = GetGroupFolder(msg.Gruop) + @"\" + headFileName;
                            headFileName = Path.Combine(clientFolder, headFileName);
                            new FileInfo(headFileName).Directory.Create();
                            File.WriteAllText(headFileName, ret);
                        }
                    }
            }
        }

        #region 获得消息的方向

        /// <summary>
        /// 获得消息的方向
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        private static string GetDirection(Message msg)
        {
            string direction;
            switch (msg.Direction)
            {
                case MessageDirection.Client2GameServer:
                    direction = "CG";
                    break;
                case MessageDirection.Client2Login:
                    direction = "CL";
                    break;
                case MessageDirection.Login2Client:
                    direction = "LC";
                    break;
                case MessageDirection.Login2GameServer:
                    direction = "LG";
                    break;
                case MessageDirection.GameServer2Client:
                    direction = "GC";
                    break;
                case MessageDirection.GameServer2Login:
                    direction = "GL";
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }
            return direction;
        }

        #endregion

        #region 获得参数列表

        /// <summary>
        /// 获得参数列表
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        private static string GetParamLists(Message msg)
        {
            var builder = new StringBuilder();

            foreach (Paramter param in msg.Params)
            {
                switch (param.CPlusType)
                {
                    case CPlusType.Int:

                        #region int

                        if (!string.IsNullOrEmpty(param.Info))
                        {
                            builder.AppendFormat("	//	{0}", param.Info);
                            builder.AppendLine();
                        }

                        builder.AppendFormat("	int {0};", param.Name);
                        builder.AppendLine();

                        #endregion

                        break;
                    case CPlusType.Float:

                        #region float

                        if (!string.IsNullOrEmpty(param.Info))
                        {
                            builder.AppendFormat("	//	{0}", param.Info);
                            builder.AppendLine();
                        }

                        builder.AppendFormat("	float {0};", param.Name);
                        builder.AppendLine();

                        #endregion

                        break;
                    case CPlusType.Char:

                        #region char

                        if (!string.IsNullOrEmpty(param.Info))
                        {
                            builder.AppendFormat("	//	{0}", param.Info);
                            builder.AppendLine();
                        }

                        builder.AppendFormat("	char {0};", param.Name);
                        builder.AppendLine();

                        #endregion

                        break;
                    case CPlusType.CharArray:
#region char[]
		                        if (!string.IsNullOrEmpty(param.Info))
                        {
                            builder.AppendFormat("	//	{0}", param.Info);
                            builder.AppendLine();
                        }

                        builder.AppendFormat("	char {0}[{1}];", param.Name, param.Size);
                        builder.AppendLine(); 
	#endregion
                        break;
                    case CPlusType.String:
                        break;
                    case CPlusType.Class:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }

            return builder.ToString();
        }

        #endregion

        #region 获得计算数据包长度的字符串

        /// <summary>
        /// 获得计算数据包长度的字符串
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        private static string GetPackageSize(Message msg)
        {
            var builder = new StringBuilder();

            foreach (Paramter param in msg.Params)
            {
                builder.AppendFormat(" + sizeof({0})", param.Name);
            }

            return builder.ToString();
        }

        #endregion

        #region 获得从网络数据里读取数据的代码

        /// <summary>
        /// 获得从网络数据里读取数据的代码
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        private static string GetReadCode(Message msg)
        {
            string lastSize = string.Empty;
            var builder = new StringBuilder();
            foreach (Paramter param in msg.Params)
            {
                switch (param.CPlusType)
                {
                    case CPlusType.Int:
                        builder.AppendFormat("  		{0} = *(int*)(data->p + sizeof(TMsgHead){1});",
                                             param.Name, lastSize);
                        lastSize += string.Format(" + sizeof({0})", param.Name);
                        builder.AppendLine();
                        break;
                    case CPlusType.Float:
                        builder.AppendFormat("  		{0} = *(float*)(data->p + sizeof(TMsgHead){1});",
                                             param.Name, lastSize);
                        lastSize += string.Format(" + sizeof({0})", param.Name);
                        builder.AppendLine();
                        break;
                    case CPlusType.Char:
                        builder.AppendFormat("  		{0} = *(char*)(data->p + sizeof(TMsgHead){1});",
                                             param.Name, lastSize);
                        lastSize += string.Format(" + sizeof({0})", param.Name);
                        builder.AppendLine();
                        break;
                    case CPlusType.CharArray:
                        builder.AppendFormat("  		memcpy({0}, (data->p + sizeof(TMsgHead){1}), sizeof({0}));",
                                             param.Name, lastSize);
                        lastSize += string.Format(" + sizeof({0})", param.Name);
                        builder.AppendLine();
                        break;
                    case CPlusType.String:
                        break;
                    case CPlusType.Class:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }

            return builder.ToString();
        }

        #endregion

        #region 设置发送缓冲区数据

        /// <summary>
        /// 设置发送缓冲区数据
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        private static string SetSendBufCode(Message msg)
        {
            string lastSize = string.Empty;
            var builder = new StringBuilder();
            foreach (Paramter param in msg.Params)
            {
                switch (param.CPlusType)
                {
                    case CPlusType.Int:
                        builder.AppendFormat("  		*(int*)(buf + sizeof(TMsgHead){1}) = {0};",
                                             param.Name, lastSize);
                        lastSize += string.Format(" + sizeof({0})", param.Name);
                        builder.AppendLine();
                        break;
                    case CPlusType.Float:
                        builder.AppendFormat("  		*(float*)(buf + sizeof(TMsgHead){1}) = {0};",
                                             param.Name, lastSize);
                        lastSize += string.Format(" + sizeof({0})", param.Name);
                        builder.AppendLine();
                        break;
                    case CPlusType.Char:
                        builder.AppendFormat("  		*(char*)(buf + sizeof(TMsgHead){1}) = {0};",
                                             param.Name, lastSize);
                        lastSize += string.Format(" + sizeof({0})", param.Name);
                        builder.AppendLine();
                        break;
                    case CPlusType.CharArray:
                        builder.AppendFormat("  		memcpy(buf + sizeof(TMsgHead){1}, {0}, sizeof({0}));",
                                             param.Name, lastSize);
                        lastSize += string.Format(" + sizeof({0})", param.Name);
                        builder.AppendLine();
                        break;
                    case CPlusType.String:
                        break;
                    case CPlusType.Class:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }

            return builder.ToString();
        }

        #endregion

        #region 获得发送数据包的静态函数的参数的函数的参数

        /// <summary>
        /// 获得发送数据包的静态函数的参数的函数的参数
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        private static string GetSendPackageParam(Message msg)
        {
            var builder = new StringBuilder();

            foreach (Paramter param in msg.Params)
            {
                switch (param.CPlusType)
                {
                    case CPlusType.Int:
                        builder.AppendFormat(", int {0}", GetParamStr(param.Name));
                        break;
                    case CPlusType.Float:
                        builder.AppendFormat(", float {0}", GetParamStr(param.Name));
                        break;
                    case CPlusType.Char:
                        builder.AppendFormat(", char {0}", GetParamStr(param.Name));
                        break;
                    case CPlusType.CharArray:
                        builder.AppendFormat(", char* {0}", GetParamStr(param.Name));
                        break;
                    case CPlusType.String:
                        break;
                    case CPlusType.Class:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }

            return builder.ToString();
        }

        #endregion

        #region 过得设置发送包裹的赋值代码

        /// <summary>
        /// 过得设置发送包裹的赋值代码
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        private static string GetPackageSet(Message msg)
        {
            var builder = new StringBuilder();

            foreach (Paramter param in msg.Params)
            {
                switch (param.CPlusType)
                {
                    case CPlusType.Int:
                        builder.AppendFormat("		package->{0} = {1};", param.Name, GetParamStr(param.Name));
                        builder.AppendLine();
                        break;
                    case CPlusType.Float:
                        builder.AppendFormat("		package->{0} = {1};", param.Name, GetParamStr(param.Name));
                        builder.AppendLine();
                        break;
                    case CPlusType.Char:
                        builder.AppendFormat("		package->{0} = {1};", param.Name, GetParamStr(param.Name));
                        builder.AppendLine();
                        break;
                    case CPlusType.CharArray:
                        builder.AppendFormat("		memcpy(package->{0}, {1}, sizeof({0}));", param.Name, GetParamStr(param.Name));
                        builder.AppendLine();
                        break;
                    case CPlusType.String:
                        break;
                    case CPlusType.Class:
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }
            }

            return builder.ToString();
        }

        #endregion

        #region 辅助

        /// <summary>
        /// 转换参数名为首字母消息
        /// 如果原先的首字母就是小写，则加下划线
        /// </summary>
        /// <param name="paramName"></param>
        /// <returns></returns>
        private static string GetParamStr(string paramName)
        {
            if (paramName[0].ToString().ToLower() == paramName[0].ToString())
                return "_" + paramName;

            return paramName[0].ToString().ToLower() + paramName.Substring(1, paramName.Length - 1);
        }

        /// <summary>
        /// 获得文件名的头部分
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        private static string GetFileNameHead(Message msg)
        {
            return string.Format("{0}_{1}", GetDirection(msg), msg.Name);
        }

        /// <summary>
        /// 获得不同分组对应存在的目录
        /// </summary>
        /// <param name="group"></param>
        /// <returns></returns>
        string GetGroupFolder(string group)
        {
            switch (group)
            {
                case "银行":
                    return "bank";
                case "移动":
                    return "Map";
                case "登录":
                    return "Login";
                case "交易所":
                    break;
                case "码头":
                    break;
                case "其它":
                    return "Other";
                default:
                    break;
            }

            return group;
        }

        #endregion
    }
}